﻿using System;
using System.IO;
using System.Net;
using System.Web;
using System.Text;
using System.Web.Hosting;
using System.Collections.Generic;
using System.Collections.Specialized;
using Microsoft.Win32.SafeHandles;

//自定义命名空间
using DreamCube.Framework.Utilities.Log;
using DreamCube.Foundation.Basic.Extensions.System;
using DreamCube.Foundation.Basic.Extensions.System.StringIO;

namespace DreamCube.Apps.WinForm.Common.DotNetWebServer
{
    public class AspNetRequest : HttpWorkerRequest
    {
        #region "私有字段"

        /// <summary>
        /// 指定当前缓存区最大的空间(静态字段)
        /// </summary>
        private static Int32 maxChunkLength = 64 * 1024;

        /// <summary>
        /// 请求上下文
        /// </summary>
        private HttpListenerContext context = null;

        /// <summary>
        /// 接受并处理请求的Server对象
        /// </summary>
        private AspNetServer server = null;

        /// <summary>
        /// 表示 URL 扩展的资源的其他路径信息。例如对于路径 /virdir/page.html/tail， pathInfo 值为 /tail。 
        /// </summary>
        private String pathInfo = String.Empty;

        /// <summary>
        /// 表示请求的文件路径（绝对路径）
        /// </summary>
        private String filePath = String.Empty;

        /// <summary>
        /// 表示请求的文件路径（相对路径）
        /// </summary>
        private String fileVirtualPath = String.Empty;

        /// <summary>
        /// 查询字符串
        /// </summary>
        private String queryString = String.Empty;

        /// <summary>
        /// 指定当前请求是否为一个文件
        /// </summary>
        private Boolean specialCaseStaticFileHeaders = true;

        /// <summary>
        /// 非正规的请求标头
        /// </summary>
        private String[][] unKnowHeaderPairs = null;

        /// <summary>
        /// 标准Http请求头
        /// </summary>
        private Dictionary<Int32,String> knowHeaderPairs = null;

        #endregion

        #region "构造方法"

        public AspNetRequest(HttpListenerContext context, AspNetServer server)
        {
            if (context == null)
                throw new ArgumentException(String.Format(Properties.Resources.ExceptionParameterNullOrEmpty, "context"));

            if (server == null)
                throw new ArgumentException(String.Format(Properties.Resources.ExceptionParameterNullOrEmpty, "server"));

            this.context = context;
            this.server = server;

            //初始化请求的参数
            this.InitialRequestDetails();
        }
        

        #endregion

        #region "实现抽象类的方法"

        /// <summary>
        /// 由运行时使用以通知 HttpWorkerRequest 当前请求的请求处理已完成。 
        /// </summary>
        public override void EndOfRequest()
        {
            try
            {
                this.context.Response.OutputStream.Close();
                this.context.Response.Close();
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.MakeLogAsync(ex);
            }

        }

        /// <summary>
        /// 将所有挂起的响应数据发送到客户端。
        /// </summary>
        /// <param name="finalFlush"></param>
        public override void FlushResponse(Boolean finalFlush)
        {
            try
            {
                this.context.Response.OutputStream.Flush();

                if (finalFlush)
                    this.context.Response.OutputStream.Close();
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.MakeLogAsync(ex);
            }
        }

        /// <summary>
        /// 请求标头中返回的 HTTP 谓词。
        /// HTTP 方法通常是 GET 或 POST，具体取决于客户端所需的操作。
        /// </summary>
        /// <returns></returns>
        public override String GetHttpVerbName()
        {
            return this.context.Request.HttpMethod;
        }

        /// <summary>
        /// 提供对请求的 HTTP 版本（如“HTTP/1.1”）的访问。
        /// </summary>
        /// <returns></returns>
        public override String GetHttpVersion()
        {
            return String.Format("HTTP/{0}.{1}",
                                 this.context.Request.ProtocolVersion.Major,
                                 this.context.Request.ProtocolVersion.Minor);
        }

        /// <summary>
        /// 请求标头中返回的服务器 IP 地址。
        /// </summary>
        /// <returns></returns>
        public override String GetLocalAddress()
        {
            String ipAddress = this.context.Request.LocalEndPoint.Address.ToString();
            if (ipAddress == "::1")
                return "127.0.0.1";
            return ipAddress;
        }

        /// <summary>
        /// 请求标头中返回的服务器端口号。
        /// </summary>
        /// <returns></returns>
        public override Int32 GetLocalPort()
        {
            return this.context.Request.LocalEndPoint.Port;
        }

        /// <summary>
        /// 请求查询字符串。
        /// </summary>
        /// <returns></returns>
        public override String GetQueryString()
        {
            return this.queryString;
        }

        /// <summary>
        /// 返回附加了查询字符串的请求标头中包含的 URL 路径(不包括主机和端口号)
        /// </summary>
        /// <returns></returns>
        public override String GetRawUrl()
        {
            return this.context.Request.RawUrl;
        }

        /// <summary>
        /// 客户端的 IP 地址。
        /// </summary>
        /// <returns></returns>
        public override String GetRemoteAddress()
        {
            String ipAddress = this.context.Request.RemoteEndPoint.Address.ToString();
            if (ipAddress == "::1")
                return "127.0.0.1";
            return ipAddress;
        }

        /// <summary>
        /// 客户端的 HTTP 端口号。
        /// </summary>
        /// <returns></returns>
        public override Int32 GetRemotePort()
        {
            return this.context.Request.RemoteEndPoint.Port;
        }

        /// <summary>
        /// 请求的 URI 的路径。
        /// 对于 URI file://computer/file.ext，绝对路径为 /file.ext 而本地路径为 \\computer\file.ext。 
        /// </summary>
        /// <returns></returns>
        public override String GetUriPath()
        {
            return this.fileVirtualPath;
        }

        /// <summary>
        /// 将标准 HTTP 标头添加到响应。
        /// </summary>
        /// <param name="index">标头索引。例如 HeaderContentLength。 </param>
        /// <param name="value"></param>
        public override void SendKnownResponseHeader(Int32 index, String value)
        {
            switch (index)
            {
                case HeaderServer:
                    value = Properties.Resources.SERVER_SOFTWARE;
                    break;
                case HeaderAcceptRanges:
                    if (value.ToLower() == "bytes")
                    {
                        this.specialCaseStaticFileHeaders = true;
                        return;
                    }
                    break;
                case HeaderExpires:
                case HeaderLastModified:
                    if (this.specialCaseStaticFileHeaders)
                        // NOTE: 如果当前的请求为一个静态的文件，则不需要输出此Http头
                        return;
                    break;
            }

            this.context.Response.Headers[HttpWorkerRequest.GetKnownResponseHeaderName(index)] = value;
        }

        /// <summary>
        /// 将指定文件的内容添加到响应并指定文件中的起始位置和要发送的字节数。
        /// </summary>
        /// <param name="handle">要发送的文件的句柄。</param>
        /// <param name="offset">文件中的起始位置。</param>
        /// <param name="length">要发送的字节数。</param>
        public override void SendResponseFromFile(IntPtr handle, Int64 offset, Int64 length)
        {
            if (length <= 0)
                return;

            try
            {
                using (var sfh = new SafeFileHandle(handle, false))
                {
                    //对于那些没有 FileShare 参数的 FileStream 的构造函数， FileShare.Read 是默认值。 
                    using (FileStream fs = new FileStream(sfh, FileAccess.Read))
                    {
                        SendResponseFromFileStream(fs, offset, length);
                    }
                }
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.MakeLogAsync(ex);
            }
        }

        /// <summary>
        /// 将指定文件的内容添加到响应并指定文件中的起始位置和要发送的字节数。
        /// </summary>
        /// <param name="filename">要发送的文件的名称。</param>
        /// <param name="offset">文件中的起始位置。</param>
        /// <param name="length">要发送的字节数。</param>
        public override void SendResponseFromFile(String filename, Int64 offset, Int64 length)
        {
            if (length <= 0)
                return;
            try
            {
                using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    SendResponseFromFileStream(fs, offset, length);
                }
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.MakeLogAsync(ex);
            }
        }

        /// <summary>
        /// 将字节数组中指定数目的字节添加到响应。
        /// </summary>
        /// <param name="data"></param>
        /// <param name="length"></param>
        public override void SendResponseFromMemory(Byte[] data, Int32 length)
        {
            this.context.Response.OutputStream.Write(data, 0, length);
        }

        /// <summary>
        /// 指定响应的 HTTP 状态代码和状态说明，例如 SendStatus(200, "Ok")。 
        /// </summary>
        /// <param name="statusCode"></param>
        /// <param name="statusDescription"></param>
        public override void SendStatus(Int32 statusCode, String statusDescription)
        {
            this.context.Response.StatusCode = statusCode;
            this.context.Response.StatusDescription = statusDescription;
        }

        /// <summary>
        /// 将非标准 HTTP 标头添加到响应。
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public override void SendUnknownResponseHeader(String name, String value)
        {
            this.context.Response.Headers.Add(name, value);
        }

        #endregion

        #region "重写其他方法

        /// <summary>
        /// 关闭与客户端的连接
        /// </summary>
        public override void CloseConnection()
        {
            try
            {
                this.context.Response.OutputStream.Close();
                this.context.Response.Close();
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.MakeLogAsync(ex);
            }
        }

        /// <summary>
        /// 获取返回当前正在执行的服务器应用程序的虚拟路径。
        /// </summary>
        /// <returns></returns>
        public override String GetAppPath()
        {
            return this.server.VirtualPath;
        }

        /// <summary>
        /// 返回当前正在执行的服务器应用程序的物理路径。
        /// </summary>
        /// <returns></returns>
        public override String GetAppPathTranslated()
        {
            return this.server.PhysicalPath;
        }

        /// <summary>
        /// 读取客户端的请求数据（在尚未预加载时）。
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public override Int32 ReadEntityBody(Byte[] buffer, Int32 size)
        {
            try
            {
                //Stream s = this.context.Request.InputStream;
                //if (s.Length == 0)
                //    return 0;
                //return s.Read(buffer, 0, size);
                return this.context.Request.InputStream.Read(buffer, 0, size);
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.MakeLogAsync(ex);
            }
            return 0;
        }

        /// <summary>
        /// 返回非标准的 HTTP 请求标头值。
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public override String GetUnknownRequestHeader(String name)
        {
            var n = this.unKnowHeaderPairs.Length;
            for (var i = 0; i < n; i++)
            {
                if (String.Compare(name, this.unKnowHeaderPairs[i][0], StringComparison.OrdinalIgnoreCase) == 0)
                    return this.unKnowHeaderPairs[i][1];
            }

            return String.Empty;
        }

        /// <summary>
        /// 获取所有非标准的 HTTP 标头的名称/值对。
        /// 标头的名称/值对的数组。
        /// </summary>
        /// <returns></returns>
        public override String[][] GetUnknownRequestHeaders()
        {
            return this.unKnowHeaderPairs;
        }

        /// <summary>
        /// 返回与指定的索引相对应的标准 HTTP 请求标头。
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public override String GetKnownRequestHeader(Int32 index)
        {
            switch (index)
            {
                case HeaderUserAgent:
                    return this.context.Request.UserAgent;
                default:
                    {
                        String value = null;
                        if (this.knowHeaderPairs.TryGetValue(index, out value))
                            return value;
                        return String.Empty;
                    }
            }
        }

        /// <summary>
        /// 从与请求关联的服务器变量词典返回单个服务器变量。
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public override string GetServerVariable(String name)
        {
            switch (name)
            {
                case "HTTPS":
                    return this.context.Request.IsSecureConnection ? "on" : "off";
                case "HTTP_USER_AGENT":
                    return this.context.Request.UserAgent;
                case "SERVER_SOFTWARE":
                    return Properties.Resources.SERVER_SOFTWARE;
                default:
                    return String.Empty;
            }
        }

        /// <summary>
        /// 在派生类中被重写时，返回所请求的 URI 的虚拟路径。
        /// </summary>
        /// <returns></returns>
        public override String GetFilePath()
        {
            return this.fileVirtualPath;
        }

        /// <summary>
        /// 返回请求的 URI 的物理文件路径（并将其从虚拟路径转换成物理路径：例如，从“/proj1/page.aspx”转换成“c:\dir\page.aspx”）
        /// </summary>
        /// <returns></returns>
        public override String GetFilePathTranslated()
        {
            return this.filePath;
        }

        /// <summary>
        /// 返回具有 URL 扩展的资源的其他路径信息。即对于路径 /virdir/page.html/tail， GetPathInfo 值为 /tail。
        /// </summary>
        /// <returns></returns>
        public override String GetPathInfo()
        {
            return this.pathInfo;
        }

        #endregion

        #region "私有方法"

        /// <summary>
        /// 把文件流输出到输出流中
        /// </summary>
        /// <param name="f"></param>
        /// <param name="offset">输出流的偏移位置</param>
        /// <param name="length">输出流的长度，-1表示默认为输出整个流 fs 的数据</param>
        private void SendResponseFromFileStream(Stream fs, Int64 offset, Int64 length = -1)
        {
            //文件流的长度
            Int64 fileSize = fs.Length;

            if (length == -1)
                length = fileSize - offset;

            if (length == 0 || offset < 0 || length > fileSize - offset)
                return;

            if (offset > 0)
                fs.Seek(offset, SeekOrigin.Begin);

            if (length <= AspNetRequest.maxChunkLength)
            {
                var fileBytes = new Byte[(Int64)length];
                var bytesRead = fs.Read(fileBytes, 0, (Int32)length);
                SendResponseFromMemory(fileBytes, bytesRead);
            }
            else
            {
                var chunk = new Byte[AspNetRequest.maxChunkLength];
                var bytesRemaining = (Int32)length;

                while (bytesRemaining > 0)
                {
                    var bytesToRead = (bytesRemaining < AspNetRequest.maxChunkLength) ? bytesRemaining : AspNetRequest.maxChunkLength;
                    var bytesRead = fs.Read(chunk, 0, bytesToRead);

                    SendResponseFromMemory(chunk, bytesRead);
                    bytesRemaining -= bytesRead;

                    // 调用flush，以释放缓冲区的内存
                    if ((bytesRemaining > 0) && (bytesRead > 0))
                        FlushResponse(false);
                }
            }
        }

        /// <summary>
        /// http标头分析处理
        /// </summary>
        private void HeadsAnalyse()
        {
            try
            {
                this.knowHeaderPairs = new Dictionary<Int32, String>();
                List<String[]> unKnowHeaders = new List<String[]>();

                NameValueCollection headers = this.context.Request.Headers;
                Int32 count = headers.Count;
                for (Int32 i = 0; i < count; i++)
                {
                    String headerName = headers.GetKey(i);
                    String headerValue = headers.Get(i);
                    Int32 headerIndex = HttpWorkerRequest.GetKnownRequestHeaderIndex(headerName);
                    if (headerIndex == -1)
                        unKnowHeaders.Add(new String[] { headerName, headerValue });
                    else
                        this.knowHeaderPairs.Add(headerIndex, headerValue);
                }
                this.unKnowHeaderPairs = unKnowHeaders.ToArray();
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.MakeLogAsync(ex);
            }
        }

        /// <summary>
        /// Url的处理
        /// </summary>
        private void UrlAnalyse()
        {
            try
            {
                String path = this.context.Request.RawUrl;

                //反序列化Url路径
                if (path.IndexOf('%') >= 0)
                    path = HttpUtility.UrlDecode(path, Encoding.UTF8);

                //计算QeuryString
                Int32 queryPlace = path.IndexOf("?");
                if (queryPlace >= 0)
                {
                    if (path.Length > queryPlace + 1)
                        this.queryString = path.Substring(queryPlace + 1);

                    //去掉查询字符串部分
                    path = path.Substring(0, queryPlace);
                }

                //计算pathInfo
                Int32 lastDot = path.LastIndexOf('.');
                Int32 lastSlh = path.LastIndexOf('/');

                if (lastDot >= 0 && lastSlh >= 0 && lastDot < lastSlh)
                {
                    var ipi = path.IndexOf('/', lastDot);
                    this.fileVirtualPath = path.Substring(0, ipi);
                    this.filePath = GetFilePhysicalPath(this.fileVirtualPath);
                    this.pathInfo = path.Substring(ipi);
                }
                else
                {
                    //需要计算默认文档
                    if (path.EndsWith("/"))
                    {
                        if (this.server.DefaultDocuments.Length == 1)
                            this.fileVirtualPath = path + this.server.DefaultDocuments[0];
                        else
                        {
                            for (Int32 i = 0; i < this.server.DefaultDocuments.Length; i++)
                            {
                                String tempVirtualPath = path + this.server.DefaultDocuments[i];
                                String filePath = GetFilePhysicalPath(tempVirtualPath);
                                if (filePath.IsFileExists())
                                {
                                    this.fileVirtualPath = tempVirtualPath;
                                    break;
                                }
                            }
                        }
                    }
                    else { this.fileVirtualPath = path; }
                    
                    this.filePath = GetFilePhysicalPath(this.fileVirtualPath);
                    this.pathInfo = String.Empty;
                }
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.MakeLogAsync(ex);
            }
        }

        /// <summary>
        /// 获取文件的物理路径
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private String GetFilePhysicalPath(String fileVirtualPath)
        {
            String filePath = fileVirtualPath.Substring(this.server.VirtualPath.Length);
            filePath = filePath.Replace('/', '\\').TrimStart('\\');
            return Path.Combine(this.server.PhysicalPath, filePath);
        }

        /// <summary>
        /// 初始化请求的参数
        /// </summary>
        private void InitialRequestDetails()
        {
            UrlAnalyse();
            HeadsAnalyse();
        }

        #endregion
    }
}
